/**
 * 
 */
package com.novartis.nims.imagedc.anonymizer;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;

import org.dcm4che.data.Dataset;
import org.dcm4che.data.DcmElement;
import org.dcm4che.dict.Tags;

/**
 * @author Josh Snyder
 *
 */
class GlobalRuleApplicator {
	
	private Dataset rootDataset;
	
	private Properties localRules;
	
	private int[] specifiedGroups = null;
	
	private boolean removePrivateGroups = false;
	private boolean removeUnspecifiedGroups = false;
	private boolean removeOverlayGroups = false;
	
	private boolean recursive = false;

	/**
	 * 
	 */
	GlobalRuleApplicator(Dataset initRootDataset, Properties initLocalRules) {
		rootDataset = initRootDataset;
		localRules = initLocalRules;
	}
	
	void execute() {
		if (removePrivateGroups || removeUnspecifiedGroups || removeOverlayGroups) {
			applyRulesToDataset(rootDataset);
		}
	}
	
	private void applyRulesToDataset(Dataset ds) {
		LinkedList<Integer> tagsToRemove = new LinkedList<Integer>();
		
		for (Iterator<?> it = ds.iterator(); it.hasNext(); ) {
			DcmElement element = (DcmElement) it.next();
			boolean removeElement = false;
			int tag = element.tag();
			
			if (! tagIsOverriddenByLocalRule(tag)) {
				
				if (
					removePrivateGroups && tagHasPrivateGroup(tag) && !(tagHasSpecifiedGroup(tag)) ||
				    removeUnspecifiedGroups && !(tagHasSpecifiedGroup(tag)) && !(tagIsKeptByDefault(tag))
				    ) {
					tagsToRemove.add(new Integer(tag));
					removeElement = true;
				}
			}
			
			if (removeOverlayGroups) {
				tagsToRemove.add(new Integer(tag));
				removeElement = true;
			}
			
			//if element is a Sequence, and it's not already being removed, recurse into its
			//child items
			if (recursive && !removeElement && element.hasItems()) {
				for (int i = 0; i < element.countItems(); i++) {
					Dataset item = element.getItem(i);
					applyRulesToDataset(item);
				}
			}
		}

		for (int tagToRemove : tagsToRemove) {
			try {
				ds.remove(tagToRemove);
			} catch (Exception ignore) { }
		}
	}
	
	private boolean tagIsOverriddenByLocalRule(int tag) {
		return localRules.getProperty(Tags.toString(tag).toLowerCase()) != null;
	}
	
	private boolean tagHasPrivateGroup(int tag) {
		return (tag & 0x10000) != 0;
	}
	
	private boolean tagIsOverlay(int tag) {
		int group = tag & 0xFFFF0000;
		return (group & 0xFF000000) == 0x60000000;
	}
	
	private boolean tagHasSpecifiedGroup(int tag) {
		int group = tag & 0xFFFF0000;
		
		return Arrays.binarySearch(specifiedGroups, group) >= 0;
	}
	
	private boolean tagIsKeptByDefault(int tag) {
		int group = tag & 0xFFFF0000;
		
		return (tag == 0x00080016) ||	//SopClassUID
			   (tag == 0x00080018) || 	//SopInstanceUID
			   (tag == 0x0020000D) ||	//StudyInstanceUID
			   (group == 0x00020000) ||	//FMI group
			   (group == 0x00280000) ||	//the image description
			   (group == 0x7FE00000) ||	//the image
			   (tagIsOverlay(tag) && !(removeOverlayGroups));
	}

	void setRemoveOverlayGroups(boolean value) {
		removeOverlayGroups = value;
	}

	void setRemovePrivateGroups(boolean value) {
		removePrivateGroups = value;
	}

	void setRemoveUnspecifiedGroups(boolean value) {
		removeUnspecifiedGroups = value;
	}

	void setSpecifiedGroups(int[] value) {
		specifiedGroups = value;
	}

	void setRecursive(boolean value) {
		recursive = value;
	}

}
